package com.hy.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.protobuf.ServiceException;
import com.hy.mapper.MedicineEvaluationLikeMapper;
import com.hy.mapper.MedicineEvaluationMapper;
import com.hy.mapper.MedicineEvaluationReplyMapper;
import com.hy.medicine.MedicineEvaluation;
import com.hy.medicine.evaluation.dto.EvaluationReplyDTO;
import com.hy.medicine.evaluation.dto.MedicineEvaluationDTO;
import com.hy.medicine.evaluation.dto.MedicineEvaluationQuery;
import com.hy.medicine.evaluation.pojo.MedicineEvaluationReply;
import com.hy.medicine.evaluation.vo.EvaluationReplyVO;
import com.hy.medicine.evaluation.vo.MedicineEvaluationVO;
import com.hy.until.PageResult;
import com.hy.until.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 陌寒    Lenovo
 * @date 2025/3/16下午10:30
 * @description 类描述
 */
@Service
@Slf4j
public class MedicineEvaluationServiceImpl implements MedicineEvaluationService {
    @Autowired
    private MedicineEvaluationMapper evaluationMapper;

    @Autowired
    private MedicineEvaluationLikeMapper likeMapper;
    @Autowired
    private MedicineEvaluationReplyMapper replyMapper;

    @Autowired
    private SensitiveWordService sensitiveWordService;


    /**
     * 添加药品评价
     *
     * @param dto 药品评价的DTO，包含评价的相关信息
     * @throws Exception 如果其他错误发生
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addEvaluation(MedicineEvaluationDTO dto) throws ServiceException {
        // 1. 敏感词过滤
        String filteredContent = sensitiveWordService.filter(dto.getEvaluationContent());

        // 2. 保存评价
        MedicineEvaluation evaluation = new MedicineEvaluation();
        BeanUtils.copyProperties(dto, evaluation);
        evaluation.setEvaluationContent(filteredContent);
        evaluation.setEvaluationStatus(0); // 待审核
        evaluation.setLikeCount(0);
        evaluation.setReplyCount(0);
        evaluation.setCreateTime(new Date());
        evaluation.setUpdateTime(new Date());

        try {
            evaluationMapper.insert(evaluation);
        } catch (Exception e) {
            log.error("保存评价失败", e);
            throw new ServiceException("评价保存失败");
        }
    }

    /**
     * 获取药品评价列表
     *
     * @param query 药品评价查询对象，包含药品ID、页码和页面大小等查询参数
     * @return 返回药品评价的分页结果
     *
     * 该方法首先构建查询条件，只查询已通过的药品评价，并按照创建时间降序排列
     * 然后进行分页查询，接着将查询结果转换为VO列表，最后返回分页结果
     */
    @Override
    public PageResult<MedicineEvaluationVO> getEvaluationList(MedicineEvaluationQuery query) {
        // 1. 构建查询条件
        LambdaQueryWrapper<MedicineEvaluation> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MedicineEvaluation::getMedicineId, query.getMedicineId())
                .eq(MedicineEvaluation::getEvaluationStatus, 1) // 只查已通过的
                .orderByDesc(MedicineEvaluation::getCreateTime);

        // 2. 分页查询
        Page<MedicineEvaluation> page = new Page<>(query.getPageNum(), query.getPageSize());
        Page<MedicineEvaluation> evaluationPage = evaluationMapper.selectPage(page, wrapper);

        // 3. 转换为VO
        List<MedicineEvaluationVO> voList = evaluationPage.getRecords().stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());

        return new PageResult<>(
            evaluationPage.getCurrent(),
            evaluationPage.getSize(),
            evaluationPage.getTotal(),
            voList
        );
    }

    /**
     * 点赞/取消点赞功能
     *
     * 此方法用于处理用户对某个评价的点赞操作如果用户已经点赞，则取消点赞；如果未点赞，则添加点赞
     * 使用了@Transactional注解，确保在出现异常时能够回滚事务
     *
     * @param evaluationId 评价的ID，用于确定用户点赞的具体评价对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void toggleLike(Integer evaluationId) throws ServiceException {
        // 检查评价是否存在
        MedicineEvaluation evaluation = evaluationMapper.selectById(evaluationId);
        if (evaluation == null) {
            throw new ServiceException("评价不存在");
        }

        // 更新点赞数
        evaluationMapper.incrementLikeCount(evaluationId);
    }

    /**
     * 添加回复功能
     * 该方法实现了对药品评价的回复功能，包括敏感词过滤、保存回复内容以及更新评价的回复数量
     *
     * @param dto 包含回复内容及相关信息的数据传输对象
     * @throws ServiceException 当回复添加过程中发生业务逻辑错误时抛出此异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addReply(EvaluationReplyDTO dto) throws ServiceException {
        // 1. 敏感词过滤
        // 对回复内容进行敏感词过滤，以确保内容的合规性
        String filteredContent = sensitiveWordService.filter(dto.getReplyContent());

        // 2. 保存回复
        // 创建一个新的药品评价回复对象，并将DTO中的属性复制到该对象中
        MedicineEvaluationReply reply = new MedicineEvaluationReply();
        BeanUtils.copyProperties(dto, reply);
        // 设置经过敏感词过滤的回复内容
        reply.setReplyContent(filteredContent);
        // 设置当前用户的ID
        reply.setUserId(SecurityUtils.getUserId());
        // 设置回复的状态为待审核
        reply.setReplyStatus(0); // 待审核
        // 将回复对象插入到数据库中
        replyMapper.insert(reply);

        // 3. 更新评价回复数
        // 更新药品评价的回复数量，以反映新的回复
        evaluationMapper.incrementReplyCount(dto.getEvaluationId());
    }

    /**
     * 获取指定评价的回复列表
     *
     * 本方法首先查询数据库中与特定评价相关的所有回复，然后将这些回复构建成一个回复树结构
     * 这样做是为了使回复具有更好的可读性和组织性，模仿评论系统中的回复结构
     *
     * @param evaluationId 评价的唯一标识符，用于查询相关的回复
     * @return 返回一个构建好的回复树结构，包含所有与指定评价相关的回复
     */
    @Override
    public List<EvaluationReplyVO> getReplies(Integer evaluationId) {
        // 1. 查询回复列表
        List<MedicineEvaluationReply> replies = replyMapper.selectRepliesByEvaluationId(evaluationId);

        // 2. 构建回复树
        return buildReplyTree(replies);
    }

    /**
     * 转换为VO
     *
     * 此方法的目的是将MedicineEvaluation对象转换为MedicineEvaluationVO对象，以便在不同层次之间传递数据
     * 它处理了数据的转换、匿名用户显示、图片列表和回复列表的加载，以及检查当前用户是否点赞
     *
     * @param evaluation MedicineEvaluation对象，包含评价的相关信息
     * @return 返回一个转换后的MedicineEvaluationVO对象
     */
    private MedicineEvaluationVO convertToVO(MedicineEvaluation evaluation) {
        MedicineEvaluationVO vo = new MedicineEvaluationVO();
        BeanUtils.copyProperties(evaluation, vo);

        // 处理匿名显示
        vo.setUserName("匿名用户");

        // 简化版本不需要获取图片列表
        vo.setImages(null);

        // 获取回复列表
        List<MedicineEvaluationReply> replies = replyMapper.selectRepliesByEvaluationId(evaluation.getEvaluationId());
        vo.setReplies(buildReplyTree(replies));

        // 所有评价默认未点赞
        vo.setIsLiked(false);

        return vo;
    }

    /**
     * 构建回复树
     *
     * 该方法的目的是将回复按照其层级关系组织成树形结构，以便于展示和处理
     * 它首先将所有回复转换为VO（Value Object）对象，并识别出根级别的回复
     * 然后，它将每个回复对象与其父回复关联起来，形成嵌套的树形结构
     *
     * @param replies 医药评价回复列表，这些回复尚未被组织成树形结构
     * @return 返回根级别的回复列表，其中包含了它们的子回复（如果有的话）
     */
    private List<EvaluationReplyVO> buildReplyTree(List<MedicineEvaluationReply> replies) {
        // 用于存储所有回复的映射，键为回复ID，值为对应的VO对象
        Map<Integer, EvaluationReplyVO> voMap = new HashMap<>();
        // 用于存储根级别的回复列表
        List<EvaluationReplyVO> rootList = new ArrayList<>();

        // 转换为VO并建立映射关系
        for (MedicineEvaluationReply reply : replies) {
            EvaluationReplyVO vo = new EvaluationReplyVO();
            BeanUtils.copyProperties(reply, vo);
            vo.setUserName("匿名用户"); // 所有回复都显示为匿名
            voMap.put(reply.getReplyId(), vo);

            if (reply.getParentId() == null) {
                rootList.add(vo);
            } else {
                EvaluationReplyVO parentVO = voMap.get(reply.getParentId());
                if (parentVO != null) {
                    if (parentVO.getChildren() == null) {
                        parentVO.setChildren(new ArrayList<>());
                    }
                    parentVO.getChildren().add(vo);
                }
            }
        }

        return rootList;
    }


}
